package model;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.EnumMap;
import java.util.LinkedList;
import model.interfaces.selectable.ControllableModel;
import model.interfaces.selectable.ControllablePlayer;

/**
 * holds everything needed to play the game and changes states based on an
 * internal timer.
 * 
 * @author Adam Szpakowski
 * 
 */
public class Model implements TileTranslator, Game, ControllableModel, ClockListener
{
    private static final long MSECS_PER_TICK = 1000;
    
    private GameMap gameMap;
    private Human humanPlayer;
    private AI aiPlayer;
    private LinkedList< GameListener > listeners;
    private Clock timer;
    private boolean tock = false;
    
    public Model( String mapFile ) throws IOException, ClassNotFoundException {
        EnumMap< ResourceType, Integer > rsc1 = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        EnumMap< ResourceType, Integer > rsc2 = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        
        rsc1.put( ResourceType.BLUE, new Integer( 999 ) );
        rsc1.put( ResourceType.RED, new Integer( 999 ) );
        rsc1.put( ResourceType.GREEN, new Integer( 999 ) );
        
        rsc2.put( ResourceType.BLUE, new Integer( 999 ) );
        rsc2.put( ResourceType.RED, new Integer( 999 ) );
        rsc2.put( ResourceType.GREEN, new Integer( 999 ) );
        
        FileInputStream fis = new FileInputStream( mapFile );
        ObjectInputStream ois = new ObjectInputStream( fis );
        gameMap = (GameMap) ois.readObject();
        
        humanPlayer = new Human( gameMap, gameMap.getStartingLocation1(), rsc1 );
        aiPlayer = new AI( gameMap, gameMap.getStartingLocation2(), rsc2 );
        
        humanPlayer.setEnemy( aiPlayer );
        aiPlayer.setEnemy( humanPlayer );
        
        listeners = new LinkedList< GameListener >();
        
        // set up timer for ticks
        timer = new StrawberryClock( MSECS_PER_TICK );
        timer.addListener( this );
    }
    
    public Model( GameMap map ) throws IOException, ClassNotFoundException {
        EnumMap< ResourceType, Integer > rsc1 = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        EnumMap< ResourceType, Integer > rsc2 = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        
        rsc1.put( ResourceType.BLUE, new Integer( 999 ) );
        rsc1.put( ResourceType.RED, new Integer( 999 ) );
        rsc1.put( ResourceType.GREEN, new Integer( 999 ) );
        
        rsc2.put( ResourceType.BLUE, new Integer( 999 ) );
        rsc2.put( ResourceType.RED, new Integer( 999 ) );
        rsc2.put( ResourceType.GREEN, new Integer( 999 ) );
        
        gameMap = map;
        
        humanPlayer = new Human( gameMap, gameMap.getStartingLocation1(), rsc1 );
        aiPlayer = new AI( gameMap, gameMap.getStartingLocation2(), rsc2 );
        
        humanPlayer.setEnemy( aiPlayer );
        aiPlayer.setEnemy( humanPlayer );
        
        listeners = new LinkedList< GameListener >();
        
        // set up timer for ticks
        timer = new StrawberryClock( MSECS_PER_TICK );
        timer.addListener( this );
    }
    
    public GameMap getGameMap()
    {
        return gameMap;
    }
    
    /**
     * returns a 2D array of MagicTile objects that the View may use.
     * 
     * @author Adam Szpakowski
     */
    public MagicTile[][] getMagicTiles( int x, int y, int w, int h )
    {
        ScienceTile[][] tiles = new ScienceTile[ w ][ h ];
        
        for ( int i = x; i < x + w; i++ )
        {
            for ( int j = y; j < y + h; j++ )
            {
                // Debug: System.out.println("Creating Science tile");
                tiles[ i - x ][ j - y ] = new ScienceTile( humanPlayer, aiPlayer,
                        gameMap.getTile( i, j ), new Location( i, j ) );
            }
        }
        
        return tiles;
    }
    
    public int getMapWidth()
    {
        return gameMap.getWidth();
    }
    
    public int getMapHeight()
    {
        return gameMap.getHeight();
    }
    
    public void tick()
    {
        if ( !tock )
            System.out.println( "TICK" );
        else
            System.out.println( "TOCK" );
        humanPlayer.onTick();
        aiPlayer.onTick();
        for ( int i = 0; i < listeners.size(); i++ )
            listeners.get( i ).onTick();
        
        tock = !tock;
    }
    
    public void addGameListener( GameListener gl )
    {
        listeners.add( gl );
    }
    
    /**
     * Ends the game.
     * 
     * @author Adam Szpakowski
     */
    public void endGame()
    {
        timer.stop();
        // TODO: losing the game (damnit) should be possible
        for ( int i = 0; i < listeners.size(); i++ )
            listeners.get( i ).onGameWin();
    }
    
    /**
     * Returns a reference to the human-controlled player object.
     * 
     * @return a reference to the controllable player.
     */
    public ControllablePlayer getPlayer()
    {
        return humanPlayer;
    }
    
    /**
     * Starts the game.
     * 
     * @author Adam Szpakowski
     */
    public void startGame()
    {
        System.out.println( "Game Started" );
        timer.start();
        for ( int i = 0; i < listeners.size(); i++ )
            listeners.get( i ).onGameBegin();
    }
    
    /**
     * Grabs a resource number from the human player to send to view
     * 
     * @author Adam Szpakowski
     */
    public int getResourceCount( ResourceType resource )
    {
        return humanPlayer.getResourceCount( resource );
    }
}
